package com.zengdada.authority.utils.excel.export;

import com.zengdada.authority.utils.excel.export.title.AllTableTitle;
import com.zengdada.authority.utils.excel.export.title.CellTableTitle;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.*;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.text.SimpleDateFormat;
import java.util.*;


/**
 * @author 12614
 * @contact 1261438511@qq.com
 * @date 2020-05-25 23:28
 * @describe 导出 excel 文件 可配置复杂表头及样式 (预设置) 且可以更具数据格式内容等做后续修改 DataTriggerHandle
 */
public class ExportExcel {

    private Map<String, List<Map>> datas;
    private LinkedHashMap<String, AllTableTitle> titles;
    public static final int NEW_EXCEL_TYPE = 0, OLD_EXCEL_TYPE = 1;
    private List<DataTriggerHandle> dataTriggers = new ArrayList<DataTriggerHandle>() {{
        add(new NotNullDataTriggerHandle() {
            @Override
            public boolean handle(Cell currentCell, Object o) {
                currentCell.setCellValue(String.valueOf(o));
                return false;
            }
        });
    }};

    private static final String DEFAULT_SHEET_NAME = "default_sheet_name";

    public ExportExcel(Map<String, List<Map>> datas, LinkedHashMap<String, AllTableTitle> titles) {
        if (titles == null && datas == null) {
            throw new IllegalArgumentException("数据 datas 和 表头信息 不能同时为空！！");
        }
        this.datas = datas;
        this.titles = titles;
    }

    /**
     * @param data   数据
     * @param titles 表头信息
     * @author 12614
     * @date 2020-05-23 22:46
     */
    public ExportExcel(List<Map> data, AllTableTitle titles) {
        this(new HashMap<>(Collections.singletonMap(DEFAULT_SHEET_NAME, data)), new LinkedHashMap<String, AllTableTitle>() {{
            put(DEFAULT_SHEET_NAME, titles);
        }});
    }

    public ExportExcel addDataTrigger(DataTriggerHandle dataTrigger) {
        if (dataTrigger != null) {
            this.dataTriggers.add(dataTrigger);
        }
        return this;
    }

    public void export(File exportFile) throws FileNotFoundException {
        export(new FileOutputStream(exportFile), NEW_EXCEL_TYPE);
    }

    public void export(File exportFile, int excelType) throws FileNotFoundException {
        export(new FileOutputStream(exportFile), excelType);
    }

    public void export(OutputStream out, int excelType) {
        /* 第一步，创建一个Workbook，对应一个Excel文件  */
        Workbook wb = null;
        try {
            if (excelType != NEW_EXCEL_TYPE && excelType != OLD_EXCEL_TYPE) {
                throw new IllegalArgumentException("excelType 配置错误：必须是 NEW_EXCEL_TYPE (xlsx) 或者 OLD_EXCEL_TYPE (xls)");
            }
            wb = excelType == NEW_EXCEL_TYPE ? new XSSFWorkbook() : new HSSFWorkbook();
            // 有表头
            if (titles != null && titles.size() != 0) {
                for (String sheetName : titles.keySet()) {
                    /* 第二步，在Workbook中添加一个sheet,对应Excel文件中的sheet  */
                    int sheetNum = 0;
                    Sheet sheet = wb.createSheet(sheetName.replaceAll("[\\\\/?*|\\[\\]]", ""));
                    // 解析表头
                    List<CellTableTitle> lastRowTableTitle = titles.get(sheetName).builder(sheet);
                    // 写数据
                    int dataStartRowNum = 0;
                    for (CellTableTitle cellTableTitle : lastRowTableTitle) {
                        dataStartRowNum = Math.max(dataStartRowNum, cellTableTitle.getX() + cellTableTitle.getHeight());
                    }

                    Row row = sheet.createRow(dataStartRowNum);
                    if (datas == null) {
                        break;
                    }
                    for (Map<String, Object> map : datas.get(sheetName)) {
                        if (map == null) {
                            break;
                        }
                        for (CellTableTitle tableTitle : lastRowTableTitle) {
                            if (tableTitle.getWidth() > 1) {
                                // 需要合并表格
                                sheet.addMergedRegion(new CellRangeAddress(row.getRowNum(), row.getRowNum(), tableTitle.getY(), tableTitle.getY() + tableTitle.getWidth() - 1));
                            }
                            Cell cell = row.createCell(tableTitle.getY());
                            Object val = map.get(tableTitle.getFieldName());
//                            setCellValue(cell, val!=null);
                            // 进行数据触发处理 （默认第一个为设置值的处理）
                            for (DataTriggerHandle dataTrigger : dataTriggers) {
                                if (dataTrigger.execute(cell, val)) {
                                    break;
                                }
                            }
                        }

                        int rowNum = row.getRowNum();
                        if (excelType == OLD_EXCEL_TYPE && 65536 - 1 <= rowNum) {
                            sheet = wb.createSheet(sheetName.replaceAll("[\\\\/?*|\\[\\]]", "") + "--" + (++sheetNum));
                            titles.get(sheetName).builder(sheet);
                            rowNum = dataStartRowNum;
                        }
                        row = sheet.createRow(rowNum + 1);
                    }
                }
            }
            // 没有表头
            else if (datas != null && datas.size() != 0) {
                for (String sheetName : datas.keySet()) {
                    /* 第二步，在Workbook中添加一个sheet,对应Excel文件中的sheet  */
                    int sheetNum = 0;
                    Sheet sheet = wb.createSheet(sheetName.replaceAll("[\\\\/?*|\\[\\]]", ""));
                    // 写数据
                    List<Map> maps = datas.get(sheetName);
                    if (maps != null && maps.size() != 0) {
                        List<String> fields = new ArrayList<>(((Map<String, Object>) maps.get(0)).keySet());
                        int rowNum = 0, cellNum;
                        Row row;
                        for (Map<String, Object> map : maps) {
                            if (excelType == OLD_EXCEL_TYPE && 65536 - 1 <= rowNum) {
                                // 设置现在的表头
                                cellNum = 0;
                                row = sheet.createRow(0);
                                for (String field : fields) {
                                    row.createCell(cellNum++).setCellValue(field);
                                }
                                sheet = wb.createSheet(sheetName.replaceAll("[\\\\/?*|\\[\\]]", "") + "--" + (++sheetNum));
                                rowNum = 0;
                            }
                            row = sheet.createRow(++rowNum);
                            Set<String> keySet = new HashSet<>(map.keySet());
                            keySet.removeAll(fields);
                            if (keySet.size() > 0) { // 有新字段
                                fields.addAll(keySet);
                            }
                            cellNum = 0;
                            for (String field : fields) {
                                Cell cell = row.createCell(cellNum++);
                                Object val = map.get(field);
                                for (DataTriggerHandle dataTrigger : dataTriggers) {
                                    if (dataTrigger.execute(cell, val)) {
                                        break;
                                    }
                                }
                            }
                        }
                        // 最后设置 表头
                        cellNum = 0;
                        row = sheet.createRow(0);
                        for (String field : fields) {
                            row.createCell(cellNum++).setCellValue(field);
                        }
                    }
                }
            }
            // 输出
            wb.write(out);
        } catch (
                IOException e) {
            e.printStackTrace();
        } finally {
            if (wb != null) {
                try {
                    wb.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }


    public static abstract class DataTriggerHandle {

        /**
         * // 是否执行处理方法
         *
         * @param o 当前数据
         * @return {@link boolean}
         * @author 12614
         * @date 2020-05-26 1:21
         */
        public abstract boolean isEnabled(Object o);

        final boolean execute(Cell currentCell, Object o) {
            if (isEnabled(o)) {
                return handle(currentCell, o);
            }
            return false;
        }

        /**
         * // 返回值为 false 时 继续后面的 DataTrigger
         *
         * @param currentCell 当前表格
         * @param o           当前数据
         * @return {@link boolean}
         * @author 12614
         * @date 2020-05-26 1:20
         */
        public abstract boolean handle(Cell currentCell, Object o);
    }

    /**
     * // 数据类型过滤处理器
     * T 指定的数据类型  如果不配者满足所有不为空的对象类型
     *
     * @author 12614
     * @date 2020-05-28 0:11
     */
    public static abstract class DataTypeTriggerHandle<T> extends DataTriggerHandle {

        private Class<Object> getT() {
            Type genericSuperclass = this.getClass().getGenericSuperclass();
            if (genericSuperclass instanceof ParameterizedType) {
                Type[] arguments = ((ParameterizedType) genericSuperclass).getActualTypeArguments();
                if (arguments == null || arguments.length == 0) {
                    return null;
                }
                return (Class<Object>) arguments[0];
            }
//                throw new RuntimeException("没有正确定义泛型类型");
            return null;
        }

        @Override
        public boolean isEnabled(Object o) {
            if (o == null) {
                return false;
            }
            Class<Object> objectClass = getT();
            if (objectClass == null) {
                return true;
            }
            return o.getClass().equals(objectClass);
        }

        @Override
        public boolean handle(Cell currentCell, Object o) {
            return dataHandle(currentCell, (T) o);
        }

        /**
         * // 返回值 false 继续后面的 DataTrigger
         *
         * @param currentCell 当前表格
         * @param o           当前数据
         * @return {@link boolean}
         * @author 12614
         * @date 2020-05-26 1:21
         */
        public abstract boolean dataHandle(Cell currentCell, T o);
    }


    public abstract static class NullDataTriggerHandle extends DataTriggerHandle {

        @Override
        public boolean isEnabled(Object o) {
            return o == null;
        }

    }

    public abstract static class NotNullDataTriggerHandle extends DataTriggerHandle {

        @Override
        public boolean isEnabled(Object o) {
            return o != null;
        }

    }
}
